package com.ry.video;

import lombok.extern.java.Log;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Log
public class SmartVideoSplitter {

    // 提取场景变化关键帧的时间戳
    public List<Double> extractSceneTimestamps(String inputVideo, double threshold) throws IOException, InterruptedException {
        List<String> cmd = new ArrayList<>();
        cmd.add("ffmpeg");
        cmd.add("-i");
        cmd.add(inputVideo);
        cmd.add("-filter:v");
        cmd.add(String.format("select='gt(scene,%f)',showinfo", threshold));
        cmd.add("-f");
        cmd.add("null");
        cmd.add("-");

        Process process = new ProcessBuilder(cmd).redirectErrorStream(true).start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

        List<Double> timestamps = new ArrayList<>();
        Pattern pattern = Pattern.compile("showinfo.*pts_time:([0-9.]+)");

        String line;
        while ((line = reader.readLine()) != null) {
            Matcher matcher = pattern.matcher(line);
            if (matcher.find()) {
                timestamps.add(Double.parseDouble(matcher.group(1)));
            }
        }

        int exitCode = process.waitFor();
        if (exitCode != 0) {
            throw new IOException("FFmpeg执行失败，错误码: " + exitCode);
        }

        // 去重并排序
        Set<Double> uniqueTimestamps = new TreeSet<>(timestamps);
        return new ArrayList<>(uniqueTimestamps);
    }

    // 获取视频总时长
    public Double getVideoDuration(String inputVideo) throws IOException, InterruptedException {
        List<String> cmd = new ArrayList<>();
        cmd.add("ffprobe");
        cmd.add("-v");
        cmd.add("error");
        cmd.add("-show_entries");
        cmd.add("format=duration");
        cmd.add("-of");
        cmd.add("default=noprint_wrappers=1:nokey=1");
        cmd.add(inputVideo);

        Process process = new ProcessBuilder(cmd).redirectErrorStream(true).start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

        String durationLine = reader.readLine();
        int exitCode = process.waitFor();

        if (exitCode != 0 || durationLine == null) {
            return null;
        }

        try {
            return Double.parseDouble(durationLine.trim());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    // 切割视频片段
    public boolean cutVideo(String inputVideo, double start, double end, String outputPath) throws IOException, InterruptedException {
        List<String> cmd = new ArrayList<>();
        cmd.add("ffmpeg");
        cmd.add("-y");
        cmd.add("-i");
        cmd.add(inputVideo);
        cmd.add("-ss");
        cmd.add(String.valueOf(start));
        cmd.add("-to");
        cmd.add(String.valueOf(end));
        cmd.add("-c:v");
        cmd.add("libx264");
        cmd.add("-c:a");
        cmd.add("aac");
        cmd.add(outputPath);
        Process process = new ProcessBuilder(cmd).redirectErrorStream(true).start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

        String durationLine = reader.readLine();
        int exitCode = process.waitFor();

        if (exitCode != 0 || durationLine == null) {
            return false;
        }

        return exitCode == 0;
    }

    // 主处理逻辑
    public SplitResult smartSplitVideo(String inputVideo, String outputPath, double threshold, double minDuration) throws IOException, InterruptedException {
        // 创建输出目录
        File baseDirFile = new File(outputPath);
        if (!baseDirFile.exists()) {
            if (!baseDirFile.mkdirs()) {
                return new SplitResult(null, "无法创建输出目录", Collections.emptyList());
            }
        }

        String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
        File outputDir = new File(outputPath, dateStr);
        if (!outputDir.exists()) {
            if (!outputDir.mkdirs()) {
                return new SplitResult(null, "无法创建输出目录", Collections.emptyList());
            }
        }

        // 提取关键帧时间戳
        List<Double> timestamps = extractSceneTimestamps(inputVideo, threshold);
        Double duration = getVideoDuration(inputVideo);

        if (duration == null) {
            return new SplitResult(null, "无法获取视频时长", Collections.emptyList());
        }

        // 构建切割点
        List<Double> cutPoints = new ArrayList<>();
        cutPoints.add(0.0);
        cutPoints.addAll(timestamps);
        cutPoints.add(duration);

        // 生成初始片段
        List<Segment> segments = new ArrayList<>();
        for (int i = 0; i < cutPoints.size() - 1; i++) {
            segments.add(new Segment(cutPoints.get(i), cutPoints.get(i + 1)));
        }

        // 向前合并法
        List<Segment> mergedSegments = new ArrayList<>();
        Segment tempSegment = segments.get(0);

        for (int i = 1; i < segments.size(); i++) {
            Segment current = segments.get(i);
            if (tempSegment.getDuration() < minDuration) {
                // 合并当前区间
                tempSegment = new Segment(tempSegment.getStart(), current.getEnd());
            } else {
                mergedSegments.add(tempSegment);
                tempSegment = current;
            }
        }

        // 添加最后一个片段
        if (tempSegment.getDuration() > 0) {
            mergedSegments.add(tempSegment);
        }

        // 切割视频
        List<String> videoFiles = new ArrayList<>();
        List<String> log = new ArrayList<>();

        for (int i = 0; i < mergedSegments.size(); i++) {
            Segment segment = mergedSegments.get(i);
            String outPath = new File(outputDir, String.format("segment_%03d.mp4", i + 1)).getPath();

            boolean ok = cutVideo(inputVideo, segment.getStart(), segment.getEnd(), outPath);
            if (ok) {
                videoFiles.add(outPath);
                log.add(String.format("生成片段%d: %.2fs - %.2fs, 时长%.2fs", i + 1, segment.getStart(), segment.getEnd(), segment.getDuration()));
            } else {
                log.add(String.format("切割片段%d失败: %.2fs - %.2fs", i + 1, segment.getStart(), segment.getEnd()));
            }
        }

        if (videoFiles.isEmpty()) {
            return new SplitResult(null, "没有生成任何有效片段", log);
        }

        return new SplitResult(videoFiles, String.format("成功切割成 %d 个片段，保存在: %s", videoFiles.size(), outputDir.getPath()), log);
    }

    // 表示一个视频片段
    class Segment {
        private double start;
        private double end;

        public Segment(double start, double end) {
            this.start = start;
            this.end = end;
        }

        public double getStart() {
            return start;
        }

        public double getEnd() {
            return end;
        }

        public double getDuration() {
            return end - start;
        }
    }

    // 表示切割结果
    class SplitResult {
        private List<String> videoFiles;
        private String message;
        private List<String> log;

        public SplitResult(List<String> videoFiles, String message, List<String> log) {
            this.videoFiles = videoFiles;
            this.message = message;
            this.log = log;
        }

        public List<String> getVideoFiles() {
            return videoFiles;
        }

        public String getMessage() {
            return message;
        }

        public List<String> getLog() {
            return log;
        }
    }

    // 简单测试示例
    public static void main(String[] args) {
        try {
            String inputVideo = "material/video1.mp4";  // 替换为实际视频路径
            String outputPath = "material/out/segment";
            double threshold = 0.1;
            double minDuration = 3.0;
            SmartVideoSplitter smartVideoSplitter = new SmartVideoSplitter();
            SplitResult result = smartVideoSplitter.smartSplitVideo(inputVideo, outputPath, threshold, minDuration);

            log.info(result.getMessage());
            for (String logEntry : result.getLog()) {
                log.info(logEntry);
            }

            if (result.getVideoFiles() != null) {
                log.info("生成的视频文件:");
                for (String file : result.getVideoFiles()) {
                    log.info("文件名：" + file);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}